#include <iostream>
#include <thread>
#include <chrono>
#include <cstring>
#include <cstdio>

#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <sys/time.h>
#include <sys/select.h>
#include <sys/sysinfo.h>

#include "myutils.h"
#include "json.hpp"
#include "NodeCCD.h"
#include "NodeHost.h"

using json = nlohmann::json;

NodeCCD *NodeCCD::mSelf = nullptr;

NodeCCD* NodeCCD::getCCD()
{
	if(mSelf == nullptr)
	{
			mSelf = new NodeCCD();
	}

	return mSelf;
}

NodeCCD::NodeCCD():
		mCloudServer(""),mCloudPort(8888)
{
		mBroadcastPort = CCD_BROADCAST_PORT;
		mTcpServerPort = CCD_TCPSERVER_PORT;

		mTcpServerFd = -1;

		mCloudClient.fd = -1;
		mCloudClient.type = CLIENT_TYPE_CCD;

		mECDCurAddr = 0xF0; // start addr for ECD
}

NodeCCD::~NodeCCD()
{
		if(mTcpServerFd > 0)
		{
				close(mTcpServerFd);
				mTcpServerFd = -1;
		}
}

bool NodeCCD::initEnv(string cloud,int port)
{
		mCloudServer = cloud;
		mCloudPort = port;
		//std::atomic_init(&mReportFlag[0],true);
		//std::atomic_init(&mReportFlag[1],true);
		mReportFlag[0] = mReportFlag[1] = true;
		
		return true;
}

bool NodeCCD::begin()
{
		mBroadcastTask = std::async(std::launch::async,&NodeCCD::taskBroadcast,this);
		mTcpServerTask = std::async(std::launch::async,&NodeCCD::taskTcpServer,this);
		mTcpCloudsTask = std::async(std::launch::async,&NodeCCD::taskTcpClouds,this);
		mNanoEventTask = std::async(std::launch::async,&NodeCCD::taskNanoEvent,this);

		return true;
}

void NodeCCD::addMsgToCloud(string msg)
{
		mToCloudMsgs.push(msg);
}
void NodeCCD::addMsgToClient(string msg)
{
		mToClientMsgs.push(msg);
}

bool NodeCCD::reportTime(int id,int secs)
{
		if(mReportFlag[id])
		{
				mReportFlag[id] = false;
				return true;
		}
		using namespace std::chrono;
		if(high_resolution_clock::now() - mLastReportTimepoint[id] >= seconds(secs))
		{
				mLastReportTimepoint[id] = high_resolution_clock::now();
				return true;
		}
		return false;
}

bool NodeCCD::handleMessage(client_conn & conn)
{
		if(conn.fd <= 0 || conn.inmsg.empty())
				return false; // maybe find seperator

		size_t msgpos = conn.inmsg.find_first_of('\n');
		std::cout << "msg pos = " << msgpos << std::endl;
		if(msgpos == string::npos) return false; // not find msg

		while(msgpos == 1)
		{
			conn.inmsg = conn.inmsg.substr(msgpos+1);

			msgpos = conn.inmsg.find_first_of('\n');
		}
		if(msgpos == string::npos) return false; // not find msg

		std::string msg = conn.inmsg.substr(0,msgpos);
		conn.inmsg = conn.inmsg.substr(msgpos+1);
		std::cout << "msg item:" << msg << std::endl;

		json inroot;
		try{
				inroot = json::parse(msg);	
		}
		catch(json::parse_error& e)
		{
				std::cerr << "json parse err:" << e.what() << std::endl;
				return false;
		}

		string cmd = inroot["cmd"];
		std::cout << "msg cmd: " << cmd << std::endl;
		if(inroot.find("resp")!=inroot.end())
		{// FIXME: just ignore response: cloud login ack,cloud heartbeat ack
				std::cout << "resp msg ok !" << std::endl;
				conn.updateAlive();
				return true;
		}
		json outroot;
		std::cout << "conn type: " << conn.type << std::endl;
		switch(conn.type)
		{
				case CLIENT_TYPE_CCD: // msg: forward msg to NANOBOARD/ECD
				case CLIENT_TYPE_APP:
						{
								conn.updateAlive();

								int dest = 0;
								if(inroot.find("dest")!=inroot.end())
								{
									dest = inroot["dest"];
									std::cout << "dest is:" << dest << std::endl;
								}
								if(dest == 0)
								{//TODO:NodeHost:: Host self
										if(cmd.compare("std_heartbeat")==0)
										{
												outroot["cmd"] = cmd;
												outroot["resp"] = {{"code",0},{"desc","ok"}};
										}
								}
								else if(dest < 0xF0)
								{//TODO:NodeHost:: To NanoBoard
#if 1
										if(NodeHost::getHost()->findAppBoard(dest))
										{
												outroot = inroot;
												outroot["from"] = conn.fd;

												string tomsg = outroot.dump();
												outroot.clear();

												//send to nano board
												std::cout << "send to nanoboard: " << tomsg << std::endl;
												NodeHost::getHost()->sendMsgToBoard(dest,tomsg);
										}
										else
										{
												std::cerr << "nanoboard addr: " << dest << " not find!" << std::endl;
										}
#endif
								}
								else
								{// To ECD devices
										if(mToECDMsgs.find(dest)==mToECDMsgs.end())
										{
												mToECDMsgs.insert(std::pair<int,std::queue<string>>(dest,std::queue<string>()));
										}
										outroot = inroot;
										outroot["from"] = conn.fd;

										string tomsg = outroot.dump() + "\n";
										outroot.clear();

										mToECDMsgs[dest].push(tomsg); // forward
								}
						}
						break;
				case CLIENT_TYPE_ECD: // msg: report in(may forward to Cloud/App), ack forward msg from cloud/app
						{
								conn.updateAlive();

								if(cmd.compare("std_report_data") == 0)
								{// add addr info then forward it
										outroot = inroot;
										outroot["addr"] = conn.ecdinfo.addr;

										string report = outroot.dump() + "\n";
										outroot.clear();

										mToClientMsgs.push(report);
										mToCloudMsgs.push(report);

										outroot["cmd"] = cmd; 
										outroot["resp"] = {{"code",0},{"desc","ok"}};
								}
								else if(cmd.compare("std_heartbeat")==0)
								{
										outroot["cmd"] = cmd;
										outroot["resp"] = {{"code",0},{"desc","ok"}};
								}
								else if(inroot.find("to") != inroot.end()) 
								{// forward ack msg
										int tofd = inroot["to"];

										if(tofd == mCloudClient.fd)
										{
												outroot = inroot;
												outroot.erase("to");

												mCloudClient.outmsgs.push(outroot.dump()+"\n");
												outroot.clear();
										}
										else
										{
												for(auto & _appc: mAppClients)
												{
														if(_appc.fd == tofd && _appc.type == CLIENT_TYPE_APP)
														{
																outroot = inroot;
																outroot.erase("to");

																_appc.outmsgs.push(outroot.dump()+"\n");
																outroot.clear();

																break;
														}
												}
										}
								}
						}
						break;
				default:
						{
								if(cmd.compare("std_login") == 0)
								{
										string devtype = inroot["device"];
										std::cout << "login type: " << devtype << std::endl;
										if(devtype.compare("APP")==0)
										{
												conn.type = CLIENT_TYPE_APP;
												conn.updateAlive();
										}
										else if(devtype.compare("ECD") == 0)
										{
												conn.type = CLIENT_TYPE_ECD;
												conn.updateAlive();

												conn.ecdinfo.addr = mECDCurAddr++; // FIXME: addr reuse problem
												conn.ecdinfo.sn = inroot["sn"];
												conn.ecdinfo.hardver = inroot["hdver"];
												conn.ecdinfo.firmver = inroot["fwver"];
												conn.ecdinfo.device = inroot["device"];
												conn.ecdinfo.name = inroot["name"]; // Type
												conn.ecdinfo.model = inroot["model"];

												// report change - has ECD online
												//mReportFlag[0].store(true);
												//mReportFlag[1].store(true);
												mReportFlag[0] = mReportFlag[1] = true;
										}
										outroot["cmd"] = cmd;
										outroot["time"] = "2018-01-05 10:00:00";
										outroot["resp"] = {{"code",0},{"desc","ok"}};
								}
						}
						break;
		}
		if(!outroot.empty())
		{
				//conn.outmsg = "ok from ccd:" + std::to_string(conn.type) + "\n";
				conn.outmsg = outroot.dump() + "\n";
				send(conn.fd,conn.outmsg.c_str(),conn.outmsg.length(),0);
				std::cout << "send outmsg: " << conn.outmsg << std::endl;
		}
		return true;
}

//::build msg
string NodeCCD::packBroadcastMsg()
{
		struct sysinfo info;
		sysinfo(&info);

		char ip[16]={0};
		utils_get_local_ip4("eth0",ip);
		//utils_get_local_ip4("ens33",ip);

		string ipstr((const char *)ip);
		json msg = {{"prot","tcp/json/1.0"},{"cmd","std_hello"},
				{"device","CCD"},{"model","rasp"},{"sn",CCD_SN},{"name",CCD_NAME},
				{"hdver",CCD_HARDWARE_VERSION},{"fwver",CCD_SOFTWARE_VERSION},
				{"ip",ipstr},{"listen",CCD_TCPSERVER_PORT},{"uptime",info.uptime}};
		return msg.dump() + "\n";
}
string NodeCCD::packHeartbeatMsg()
{
		json msg = {{"prot","tcp/json/1.0"},{"cmd","std_heartbeat"}};
		return msg.dump() + "\n";
}
string NodeCCD::packCloudloginMsg()
{
		json msg = {{"prot","tcp/json/1.0"},{"cmd","std_login"},
				{"device","CCD"},{"model","rasp"},{"sn",CCD_SN},{"name",CCD_NAME},
				{"hdver",CCD_HARDWARE_VERSION},{"fwver",CCD_SOFTWARE_VERSION}};
		return msg.dump() + "\n";
}

string NodeCCD::packReportData()
{
		json report;

		report["cmd"] = "std_report_data";
		report["addr"] = 0;
		report["sn"] = "10001";
		report["time"] = "2018-01-08 15:18:00";

		report["list"] = json::array();

		//TODO:NodeHost:: nanoboard list
#if 0
		report["list"][0] = {{"sn","001"},{"addr",1},{"name","NanoSensor"},{"device","ECM"},{"model","ATmega328"},
				{"hdver","1.0.0.1"},{"fwver","1.0.0.1"}};
		report["list"][1] = {{"sn","002"},{"addr",2},{"name","NanoRelay"},{"device","ECM"},{"model","ATmega328"},
				{"hdver","1.0.0.1"},{"fwver","1.0.0.1"}};
#else		
		NodeAppBoard boards[10];
		int num = 0;
		if((num=NodeHost::getHost()->getAppBoard(boards))>0)
		{
				for(int i=0;i<num;i++)
				{
						json info;

						info["sn"] = boards[i].appid(); 
						info["addr"] = boards[i].addr();
						info["name"] = boards[i].model();
						info["device"] = "ECM";
						info["model"] = "ATmega328";
						info["hdver"] = boards[i].hardver();
						info["fwver"] = boards[i].firmver();

						report["list"].push_back(info);
				}
		}
#endif
		//ECD list
		for(auto& _ecditem: mAppClients)
		{
				if(_ecditem.type == CLIENT_TYPE_ECD)
				{
						json info;
						info["sn"] = _ecditem.ecdinfo.sn;
						info["addr"] = _ecditem.ecdinfo.addr;
						info["name"] = _ecditem.ecdinfo.name;
						info["device"] = _ecditem.ecdinfo.device;
						info["model"] = _ecditem.ecdinfo.model;
						info["hdver"] = _ecditem.ecdinfo.hardver;
						info["fwver"] = _ecditem.ecdinfo.firmver;

						report["list"].push_back(info);
				}
		}

		report["data"] = json::array();

		//TODO:NodeHost::data info
#if 0
		report["data"][0] = {{"key","temprature"},{"value",10},{"unit","C"}};
		report["data"][1] = {{"key","humidity"},{"value",80},{"unit","%"}};
#else
		report["data"][0] = {{"key","temprature"},{"value",NodeHost::getHost()->readTemprature()},{"unit","C"}};
		report["data"][1] = {{"key","humidity"},{"value",NodeHost::getHost()->readHumidity()},{"unit","%"}};
#endif

		return report.dump() + "\n";
}

void NodeCCD::taskBroadcast()
{
		int fd = -1;

		fd = socket(AF_INET,SOCK_DGRAM,0);
		if(fd < 0)
		{
				std::cerr << "create broadcast err!" << std::endl;
				std::terminate();
		}
		std::cout << "broadcast fd = " << fd << std::endl;

		int broadcast = 1;
		setsockopt(fd,SOL_SOCKET,SO_BROADCAST,(void*)&broadcast,sizeof(int));

		struct sockaddr_in selfaddr,destaddr;
		memset(&selfaddr,0,sizeof(selfaddr));
		memset(&destaddr,0,sizeof(selfaddr));

		selfaddr.sin_family = AF_INET;
		selfaddr.sin_addr.s_addr = htonl(INADDR_ANY);
		selfaddr.sin_port = htons(1507);

		bind(fd,(struct sockaddr*)&selfaddr,sizeof(selfaddr));

		destaddr.sin_family = AF_INET;
		destaddr.sin_addr.s_addr = INADDR_BROADCAST;
		destaddr.sin_port = htons(mBroadcastPort);

		std::string sndmsg;
		while(true)
		{
				//step 1: build broadcast msg
				sndmsg = packBroadcastMsg();
				//step 2: sendto
				sendto(fd,sndmsg.c_str(),sndmsg.length(),0,(struct sockaddr*)&destaddr,sizeof(destaddr));

				std::this_thread::sleep_for(std::chrono::seconds(10));
		}
		close(fd);
}
void NodeCCD::taskTcpServer()
{
		mTcpServerFd = socket(AF_INET,SOCK_STREAM,0);
		if(mTcpServerFd < 0)
		{
				std::cerr << "create tcp server sock fail!" << std::endl;
				std::terminate();
		}
		struct sockaddr_in servaddr={0};
		servaddr.sin_family = AF_INET;
		servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
		servaddr.sin_port = htons(mTcpServerPort);

		if(bind(mTcpServerFd,(struct sockaddr*)&servaddr,sizeof(servaddr))<0)
		{
				std::cerr << "bind error" << std::endl;
				std::terminate();
		}

		int reuse = 1;
		int& fd = mTcpServerFd;
		setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,(char*)&reuse,sizeof(int));

		struct timeval tout = {3,0};
		setsockopt(fd,SOL_SOCKET,SO_RCVTIMEO,(char*)&tout,sizeof(tout));
		setsockopt(fd,SOL_SOCKET,SO_SNDTIMEO,(char*)&tout,sizeof(tout));

		listen(fd,10);

		struct sockaddr_in cliaddr;
		socklen_t addrlen = sizeof(cliaddr);

		fd_set rdfds;
		fd_set wdfds;

		FD_ZERO(&rdfds);
		FD_ZERO(&wdfds);

		int cfd = -1;
		int maxfd = fd;
		char sndbuffer[1024]={0};
		char rcvbuffer[1024]={0};
		int rcvlen = 0;
		int ret;
		std::cout << "tcp server : " << mTcpServerPort << std::endl;
		while(true)
		{
				FD_ZERO(&rdfds);
				FD_SET(fd,&rdfds);
				maxfd = fd;

				//std::cout << "clients size: " << mAppClients.size() << std::endl;
				for(auto it = mAppClients.begin();it!=mAppClients.end();)
				{
					auto& _appcli = *it;
					if(_appcli.expired())
					{
							std::cerr << "tcpserv cli(fd-" << _appcli.fd << ",type-" << _appcli.type << 
									") expire, discon & erase !" << std::endl;
							_appcli.disconnect();
							if(_appcli.type == CLIENT_TYPE_ECD)
							{// report change
								//mReportFlag[0].store(true);
								//mReportFlag[1].store(true);
								mReportFlag[0] = mReportFlag[1] = true;
							}

							mAppClients.erase(it++);
							continue;
					}
					else it++;

					if(_appcli.fd > 0) 
					{
						FD_SET(_appcli.fd,&rdfds);
						maxfd = (maxfd > _appcli.fd)?maxfd:_appcli.fd;

						//std::cout << "client fd :" << _appcli.fd << ",maxfd :" << maxfd << std::endl;

						// report data /ack msg to apps 
						if(_appcli.type == CLIENT_TYPE_APP)
						{
								while(!_appcli.outmsgs.empty())
								{
										string _tomsg = _appcli.outmsgs.front();
										send(_appcli.fd,_tomsg.c_str(),_tomsg.size(),0);

										std::cout << "ack to app:" << _tomsg << std::endl;
										_appcli.outmsgs.pop();
								}
						}
						else if(_appcli.type == CLIENT_TYPE_ECD)
						{// send cloud/app msg to ecd from forward
								if(mToECDMsgs.find(_appcli.ecdinfo.addr)!=mToECDMsgs.end())
								{
										std::queue<string>& msglist = mToECDMsgs[_appcli.ecdinfo.addr];
										while(!msglist.empty())
										{
												string report = msglist.front();
												send(_appcli.fd,report.c_str(),report.size(),0);

												msglist.pop();
										}
								}
						}
					}
				}

				// msg to all app clients online, report msg mainly
				if(reportTime(1,10))
				{
						string report = packReportData();
						for_each(mAppClients.cbegin(),mAppClients.cend(),[&](const client_conn& _appcli){
										if(_appcli.type == CLIENT_TYPE_APP)
										{
											send(_appcli.fd,report.c_str(),report.size(),0);
										}
									});
				}
				while(!mToClientMsgs.empty())
				{
						string report = mToClientMsgs.front();

						for_each(mAppClients.cbegin(),mAppClients.cend(),[&](const client_conn& _appcli){
										if(_appcli.type == CLIENT_TYPE_APP)
										{
											send(_appcli.fd,report.c_str(),report.size(),0);
										}
									});
					//	for(auto it = mAppClients.begin();it!=mAppClients.end();it++)
					//	{
					//			auto &_appcli = *it;
					//			if(_appcli.type == CLIENT_TYPE_APP)
					//			{
					//					send(_appcli.fd,report.c_str(),report.size(),0);
					//			}
					//	}

						mToClientMsgs.pop();
				}

				tout.tv_sec = 1;
				tout.tv_usec = 0;
				ret = select(maxfd+1,&rdfds,NULL,NULL,&tout);
				if(ret > 0)
				{
						if(FD_ISSET(fd,&rdfds))
						{
								cfd = accept(fd,(struct sockaddr*)&cliaddr,&addrlen);
								if(cfd > 0)
								{
										printf("client %d addr:%s\n",cfd,inet_ntoa(cliaddr.sin_addr));
										mAppClients.push_back(client_conn(cfd,0));
								}
						}
						else 
						{
								for(auto it = mAppClients.begin();it!=mAppClients.end();)
								{
										auto& _appcli = *it;
										if(_appcli.fd > 0 && FD_ISSET(_appcli.fd,&rdfds))
										{
												memset(rcvbuffer,0,sizeof(rcvbuffer));
												memset(sndbuffer,0,sizeof(sndbuffer));

												rcvlen = recv(_appcli.fd,rcvbuffer,sizeof(rcvbuffer),0);
												if(rcvlen <= 0)
												{
														std::cout << "(fd:" << _appcli.fd << ",type:" << _appcli.type 
																<< ") recv ret = " << rcvlen << std::endl;
														_appcli.disconnect();
														if(_appcli.type == CLIENT_TYPE_ECD)
														{// report change
																//mReportFlag[0].store(true);
																//mReportFlag[1].store(true);
																mReportFlag[0] = mReportFlag[1] = true;
														}

														mAppClients.erase(it++); // ensure iterator is corrent after erase.
														continue;
												}
												else
												{
														std::cout << "(fd:" << _appcli.fd << ",type:" << _appcli.type 
																<< ") recv data : " << rcvbuffer << std::endl;
														_appcli.inmsg += string(rcvbuffer);
														//_appcli.inmsgs.push_back(string(rcvbuffer));

														// process msg 
														handleMessage(_appcli);
												}
										}
										it++; // continue next client normally
								}
						}
				}
		}
		close(mTcpServerFd);
		mTcpServerFd = -1;
}

void NodeCCD::taskTcpClouds()
{
		int fd = -1;

		struct sockaddr_in cloudaddr;
		cloudaddr.sin_family = AF_INET;
		cloudaddr.sin_addr.s_addr = inet_addr(mCloudServer.c_str());
		cloudaddr.sin_port = htons(mCloudPort);
		
		struct timeval tout = {3,0};
		bool connected = false;
		int ret = 0;
		fd_set rdfds;
		int maxfd = 0;
		char rcvbuf[1024],sndbuf[1024];
		int rcvlen = 0;
		while(true)
		{
				if(fd < 0)
				{
					fd = socket(AF_INET,SOCK_STREAM,0);
					if(fd < 0)
					{
							std::cerr << "tcp cloud client fail!" << std::endl;
							std::this_thread::sleep_for(std::chrono::seconds(5));
							continue;
					}
					setsockopt(fd,SOL_SOCKET,SO_RCVTIMEO,(void*)&tout,sizeof(tout));
					setsockopt(fd,SOL_SOCKET,SO_SNDTIMEO,(void*)&tout,sizeof(tout));

					connected = false;
					mCloudClient.fd = fd;
				}
				if(!connected)
				{
					ret = connect(fd,(struct sockaddr*)&cloudaddr,sizeof(cloudaddr));
					if(ret == 0)
					{
							std::cout << "tcp cloud connect ok,now login!" << std::endl;
							connected = true;

							string login = packCloudloginMsg();
							send(fd,login.c_str(),login.size(),0);

							mCloudClient.updateAlive();
					}
					else 
					{
						std::cerr << "connect cloud err:" << strerror(errno) << std::endl;
						std::this_thread::sleep_for(std::chrono::seconds(5));
					}
				}
				else if(fd > 0)
				{
						if(mToCloudMsgs.size() > 0)
						{
								while(!mToCloudMsgs.empty())
								{// ECD / NanoBoard Report
										string report = mToCloudMsgs.front();
										send(fd,report.c_str(),report.size(),0);

										mToCloudMsgs.pop();
								}
						}
						else if(!mCloudClient.outmsgs.empty())
						{
								while(!mCloudClient.outmsgs.empty())
								{
										string _tomsg = mCloudClient.outmsgs.front();
										send(fd,_tomsg.c_str(),_tomsg.size(),0);

										std::cout << "ack to cloud: " << _tomsg << std::endl;
										mCloudClient.outmsgs.pop();
								}
						}
						else if(reportTime(0,10))
						{//Node Self Report
								string report = packReportData();
								send(fd,report.c_str(),report.size(),0);
						}
						else if(mCloudClient.heartTime(15))
						{
								string heart = packHeartbeatMsg();
								send(fd,heart.c_str(),heart.size(),0);
						}

						FD_ZERO(&rdfds);
						FD_SET(fd,&rdfds);

						tout.tv_sec = 1;
						tout.tv_usec = 0;

						maxfd = fd;
						ret = select(maxfd+1,&rdfds,NULL,NULL,&tout);
						if(ret > 0)
						{
							memset(sndbuf,0,sizeof(sndbuf));
							memset(rcvbuf,0,sizeof(rcvbuf));
							rcvlen = recv(fd,rcvbuf,sizeof(rcvbuf),MSG_DONTWAIT);
							if(rcvlen > 0)
							{
									std::cout << "recv msg:" << rcvbuf << std::endl;
									mCloudClient.inmsg += string(rcvbuf);

									// process msg 
									handleMessage(mCloudClient);
							}
							else
							{
									std::cerr << "recv err:" << strerror(errno) << std::endl;
									mCloudClient.disconnect();
									fd = -1;
							}
						}
				}
		}
		mCloudClient.disconnect();
}

void NodeCCD::taskNanoEvent()
{
		string event;
		while(true)
		{
				if(NodeHost::getHost()->eventFromBoard(event))
				{//NodeHost
						json inroot;
						try{
								inroot = json::parse(event);	
						}
						catch(json::parse_error& e)
						{
								std::cerr << "json parse err:" << e.what() << std::endl;
								continue;
						}
						string cmd = inroot["cmd"];
						if(cmd.compare("host_event_insert_board")==0 || cmd.compare("host_event_remove_board")==0)
						{
								mReportFlag[0] = mReportFlag[1] = true;
						}
						else if(cmd.find("report_data")!=string::npos)
						{// notify event
								mToClientMsgs.push(event);
								mToCloudMsgs.push(event);
						}
						else if(inroot.find("to") != inroot.end()) 
						{// forward ack msg
								std::cout << "nano ack: " << event << std::endl;
								int tofd = inroot["to"];

								if(tofd == 0)
										; // NodeHost Self
								if(tofd == mCloudClient.fd)
								{
										json outroot = inroot;
										outroot.erase("to");

										mCloudClient.outmsgs.push(outroot.dump()+"\n");
								}
								else
								{
										for(auto & _appc: mAppClients)
										{
												if(_appc.fd == tofd && _appc.type == CLIENT_TYPE_APP)
												{
														json outroot = inroot;
														outroot.erase("to");

														_appc.outmsgs.push(outroot.dump()+"\n");

														break;
												}
										}
								}
						}
				}
				else
				{
						std::this_thread::sleep_for(std::chrono::milliseconds(10));
				}
		}
}

